#include "StdAfx.h"
#include "P2PProtocolImpl.h"
#include "../net/client.h"
#include "../log/logmgr.h"
#include "../net/netmgr.h"

INSTANCE_SINGLETON(CP2PProtocolImpl)

CP2PProtocolImpl::CP2PProtocolImpl(void)
{
}

CP2PProtocolImpl::~CP2PProtocolImpl(void)
{
}

void CP2PProtocolImpl::OnRecv_LoginReq( LoginReq& rstProtocol )
{
    if (!g_current_pkg_client)
    {
        return;
    }

    LoginAck stAck;

    if (g_current_pkg_client->get_p2p_id() && CClientMgr::Instance()->FindClient(g_current_pkg_client->get_p2p_id()))
    {
        stAck.err_code = 1;
        goto END_DEAL;
    }

    if (!CClientMgr::Instance()->AddClient(g_current_pkg_client))
    {
        stAck.err_code = 2;
        goto END_DEAL;
    }

    g_current_pkg_client->set_private_ip(rstProtocol.local_ip);
    g_current_pkg_client->set_private_port(rstProtocol.local_port);

    stAck.err_code = 0;

END_DEAL:
    stAck.p2p_id = g_current_pkg_client->get_p2p_id();
    stAck.public_ip = g_current_pkg_client->get_public_ip();
    stAck.public_port = g_current_pkg_client->get_public_port();
    stAck.rand_key = 0;

    g_current_pkg_client->SendPkg(&stAck);

    if (stAck.err_code)
    {
        g_current_pkg_client->Disconnect();
    }
}

void CP2PProtocolImpl::OnRecv_NATTestReq( NATTestReq& rstProtocol )
{
    if (!g_current_nat_test_session)
    {
        return;
    }

    CClient* client = CClientMgr::Instance()->FindClient(rstProtocol.p2p_id);
    if (!client)
    {
        g_current_nat_test_session->Disconnect();
        return;
    }

    if (g_current_nat_test_session->peer_ip() != client->get_public_ip() ||
        g_current_nat_test_session->peer_port() != client->get_public_port())
    {
        client->set_nat_type(SYMMETRIC_NAT);
    }
    else
    {
        char local_ip[32] = {0};
        char peer_ip[32] = {0};

        sockaddr_in addr;
        addr.sin_addr.s_addr = CNetMgr::Instance()->get_net_mgr()->GetLocalIP(client->get_session());
        strcpy(local_ip, inet_ntoa(addr.sin_addr));

        addr.sin_addr.s_addr = client->get_public_ip();
        strcpy(peer_ip, inet_ntoa(addr.sin_addr));

        UINT16 peer_port = ntohs(client->get_public_port());

        HSESSION session = CNetMgr::Instance()->get_net_mgr()->Connect(peer_ip, peer_port, 1024, 1024, NULL, local_ip);

        if (!session)
        {
            client->set_nat_type(PORT_RESTRICTED_CONE_NAT);
        }
        else
        {
            CConeTestSession* cone_test_session = CNetMgr::Instance()->get_cone_test_session_pool().AllocObj();
            cone_test_session->Reset();
            cone_test_session->set_session(session);
            cone_test_session->set_p2p_id(client->get_p2p_id());
            CNetMgr::Instance()->get_net_mgr()->SetSessionUserData(session, cone_test_session);
        }
    }

    g_current_nat_test_session->Disconnect();
}

void CP2PProtocolImpl::OnRecv_ConnectPeerReq( ConnectPeerReq& rstProtocol )
{
    if (!g_current_pkg_client || g_current_pkg_client->get_nat_type() == UNKNOW_NAT)
    {
        return;
    }

    CClient* dst_client = CClientMgr::Instance()->FindClient(rstProtocol.dst_p2p_id);

    if (!dst_client || dst_client->get_nat_type() == UNKNOW_NAT)
    {
        return;
    }

    ConnectPeerAck stAck;
    stAck.dst_p2p_id = dst_client->get_p2p_id();
    stAck.dst_public_ip = dst_client->get_public_ip();
    stAck.dst_public_port = dst_client->get_public_port();
    stAck.dst_private_ip = dst_client->get_private_ip();
    stAck.dst_private_port = dst_client->get_private_port();

    g_current_pkg_client->SendPkg(&stAck);

    PeerConnectReq stReq;
    stReq.peer_p2p_id = g_current_pkg_client->get_p2p_id();
    stReq.peer_public_ip = g_current_pkg_client->get_public_ip();
    stReq.peer_public_port = g_current_pkg_client->get_public_port();
    stReq.peer_private_ip = g_current_pkg_client->get_private_ip();
    stReq.peer_private_port = g_current_pkg_client->get_private_port();

    dst_client->SendPkg(&stReq);
}

bool CP2PProtocolImpl::HandleClientPkg( char* net_data, int net_size )
{
    return HandleProtocol(net_data, net_size, m_recv_pkg_cache);
}

int CP2PProtocolImpl::BuildPkg( void* host, char*& net )
{
    net = m_send_pkg_cache;

    int& pkg_len = *(int*)m_send_pkg_cache;

    pkg_len = BuildProtocol(host, m_send_pkg_cache+sizeof(int), sizeof(m_send_pkg_cache)-sizeof(int));

    if (pkg_len < 0)
    {
        return -1;
    }

    pkg_len += sizeof(int);

    return pkg_len;
}


